# -*- coding: utf-8 -*-
import os
import uuid

import aiofiles
from fastapi import Request, UploadFile, File, Form, BackgroundTasks
from fastapi.responses import FileResponse
from pydantic import BaseModel
from loguru import logger

from schemas.response_entity import JsonModel
from configs import UPLOAD_FILE_DIR
from utils.hash_utils import cal_md5
from utils.file_utils import normalize_filename_extension, to_abs_path, convert_docx_to_pdf
from db.repository.accident_case_file import db_get_ac_file_by_name, db_add_ac_file, db_get_ac_files, \
    db_get_ac_file_by_id, db_update_ac_file_delete_state, db_update_ac_file_info, db_get_ac_files_by_user_groups

from .utils import parse_ac_pdf_background


async def upload_accident_case_file(
        request: Request,
        background: BackgroundTasks,
        token=Form(...),
        file: UploadFile = File(...),  # 必需文件参数放前面
):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()

    # 验证文件类型
    allowed_extensions = {'pdf', 'doc', "docx", "DOC", "DOCX", "PDF"}
    normalized_filename = normalize_filename_extension(file.filename)
    if '.' in normalized_filename:
        file_suffix = normalized_filename.rsplit('.', 1)[-1]
    else:
        file_suffix = ""

    if file_suffix not in allowed_extensions:
        return JsonModel(code=400, data=None,
                         msg=f"不支持的文件类型: {file_suffix}，仅支持 {', '.join(allowed_extensions)}").to_response()

    # 计算文件的 MD5 值与文件大小
    file_content = await file.read()
    file_md5 = cal_md5(file_content)
    file_size = len(file_content)

    # 基础保存目录
    dir_path = os.path.join(UPLOAD_FILE_DIR, file_md5)
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

    # 针对 doc/docx：先保存原文件，再转换为 PDF；数据库中保存 PDF 的 hash_name 和 path
    if file_suffix in {"doc", "docx", "DOC", "DOCX"}:
        original_path = os.path.join(dir_path, normalized_filename)
        try:
            async with aiofiles.open(original_path, "wb") as f:
                await f.write(file_content)
        except IOError as e:
            logger.error(f"原始文件保存失败: {str(e)}")
            return JsonModel(code=500, data=None, msg="原始文件保存失败").to_response()

        pdf_hash_name = f"{file_md5}.pdf"
        pdf_path = os.path.join(dir_path, pdf_hash_name)

        # 若不存在 PDF，执行转换
        if not os.path.exists(pdf_path):
            ok = convert_docx_to_pdf(original_path, pdf_path)
            if not ok:
                logger.error(f"Word 转 PDF 失败: {original_path}")
                return JsonModel(code=500, data=None, msg="Word 转 PDF 失败").to_response()

        file_hash_name = pdf_hash_name
        file_hash_path = pdf_path
        file_suffix = "pdf"

    else:
        # PDF：直接以 MD5 命名保存
        file_hash_name = f"{file_md5}.pdf"
        file_hash_path = os.path.join(dir_path, file_hash_name)
        try:
            async with aiofiles.open(file_hash_path, "wb") as f:
                await f.write(file_content)
        except IOError as e:
            logger.error(f"文件保存失败: {str(e)}")
            return JsonModel(code=500, data=None, msg="文件保存失败").to_response()

    # 通过 hash_name 判断是否已存在
    file_obj = db_get_ac_file_by_name(file_hash_name)
    if file_obj:
        return JsonModel(code=403, data=None, msg="文件已存在，关联数据库即可").to_response()

    file_obj, is_delete = db_add_ac_file(
        user.id,
        file_hash_name,
        normalized_filename,
        file_hash_path,
        file_suffix,
        file_size,
    )
    background.add_task(parse_ac_pdf_background, file_obj, file_hash_path, delete_old_file=True)
    return JsonModel(code=200, data={"id": str(file_obj.id)}, msg=f"上传文件:{normalized_filename}成功").to_response()


async def list_ac_files(request: Request, page: int = 1, page_size: int = 50, name: str = ""):
    user = request.state.user
    try:
        offset = (page - 1) * page_size
        if user.check_superman_role():
            files, total = db_get_ac_files(name_like=name or None, offset=offset, limit=page_size)
        else:
            # 非超级管理员只能查看该用户所在组的可查看文件
            from db.repository.user import db_get_user_group_ids
            user_group_ids = db_get_user_group_ids(user)
            files, total = db_get_ac_files_by_user_groups(user_group_ids, name_like=name or None, offset=offset, limit=page_size)
        
        return JsonModel(
            code=200, 
            data={
                "items": [f.to_json() for f in files],
                "total": total,
                "page": page,
                "page_size": page_size
            }, 
            msg="success"
        ).to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


async def get_ac_file_by_id(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        f = db_get_ac_file_by_id(file_id)
        if not f or getattr(f, 'is_delete', False):
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=f.to_json(), msg="success").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


class UpdateACFileEntity(BaseModel):
    file_id: uuid.UUID
    name: str | None = None
    accident_level: str | None = None
    report_date: str | None = None
    related_company: str | None = None
    accident_abstract: str | None = None
    accident_type: str | None = None


async def update_ac_file(request: Request, payload: UpdateACFileEntity):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    if not any([
        payload.name,
        payload.accident_level,
        payload.report_date,
        payload.related_company,
        payload.accident_abstract,
        payload.accident_type is not None,
    ]):
        return JsonModel(code=300, data=None, msg="缺少更新内容").to_response()
    try:
        f = db_update_ac_file_info(
            payload.file_id,
            name=payload.name,
            accident_level=payload.accident_level,
            report_date=payload.report_date,
            related_company=payload.related_company,
            accident_abstract=payload.accident_abstract,
            accident_type=payload.accident_type,
        )
        if not f:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=None, msg="更新成功").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="更新失败").to_response()


async def delete_ac_file(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        success = db_update_ac_file_delete_state(file_id, True)
        if not success:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=None, msg="删除成功").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="删除失败").to_response()


async def download_ac_original(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        f = db_get_ac_file_by_id(file_id)
        if not f or getattr(f, 'is_delete', False):
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        abs_loc = to_abs_path(getattr(f, 'location', ''))
        if not abs_loc or not os.path.exists(abs_loc):
            return JsonModel(code=404, data=None, msg="文件未找到或已被清理").to_response()
        return FileResponse(abs_loc, media_type="application/pdf", filename=getattr(f, 'name', 'file'))
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="下载失败").to_response()
